package tau_sadna.tau_map;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import android.util.Log;

public class Search{
	//returns array of possible matches for target in 'layers', sorted in order of likelihood.
	//returns null if no matches or target string is too long (50 characters)
	//array will be of at most 'max_len' results
	public static Result[] search(String target,List<TauLayer> layers,int max_len,MainActivity mainAct){
		if (target.length()>50 || target.length()<=0){
			return null;
		}
		target = target.toLowerCase();
		//int max_error = Math.min(4,(target.length()-1)/3+1);
		int max_error = 1;
		SearchData temp = null;
		ArrayList<SearchData> result = new ArrayList<SearchData>();
		for(TauLayer layer : layers){
			if(layer.display){//only show displayable layers as complete layers
				temp = searchLayer(target,max_error,layer);
				if(temp.layer != null){
					result.add(temp);
				}
			}
			for(LayerItem item : layer.items){//show items
				temp = searchItem(target, max_error, item, mainAct);
				if(temp.layer != null){
					temp.layer.pic_name = layer.pic_name;
					result.add(temp);
				}				
			}
		}		
		//no match
		if(result.size() == 0){
			return null;
		}
		//sort
		Collections.sort(result, new Comparator<SearchData>() {
			  public int compare(SearchData o1, SearchData o2) {
			      return ((Integer)o1.distace).compareTo(o2.distace);
			  }
			});
		//trim size
		while(result.size()>max_len){
			result.remove(result.size()-1);
		}
		return result.toArray(new Result[0]);
	}
	//returns Data with layer==null if no match
	private static SearchData searchLayer(String target, int max_err,TauLayer lay){
		if(!lay.display){
			return new SearchData(-1,null,null);
		}
		String bestMatch = null;
		int dist = matchString(target,lay.name_en,max_err);
		if (dist != -1){//no match
			bestMatch = lay.name_en;
		}
		int temp_dist = matchString(target,lay.name_he,max_err);
		if((temp_dist!=-1)&&((dist==-1)||(temp_dist<dist))){
			bestMatch = lay.name_he;
			dist = temp_dist;
		}
		
		if(dist == -1){
			return new SearchData(dist,null,null);
		}else{
			return new SearchData(dist,bestMatch,lay);
		}
	}
	//returns Data with layer==null if no match
	private static SearchData searchItem(String target, int max_err,LayerItem item, MainActivity mainAct){
		String bestMatch = null;
		int dist = matchString(target,item.en,max_err);
		if (dist != -1){//no match
			bestMatch = item.en;
		}
		Log.i("Search","Searching for "+target+" in item "+item.he);
		int temp_dist = matchString(target,item.he,max_err);
		if((temp_dist!=-1)&&((dist==-1)||(temp_dist<dist))){
			bestMatch = item.he;
			dist = temp_dist;
		}
		for(String name:item.other_names){
			temp_dist = matchString(target,name,max_err);
			if((temp_dist!=-1)&&((dist==-1)||(temp_dist<dist))){
				bestMatch = name;
				dist = temp_dist;
			}
		}
		if(dist == -1){//no match
			return new SearchData(dist,null,null);
		}else{//make 'soliter' layer and return
			TauLayer lay = new TauLayer(item.en, item.he,true);//true - search result must be displayable
			lay.items.add(item);
			lay.pic_name = "marker_red";
			lay.finishedPopulating(mainAct);
			return new SearchData(dist,bestMatch,lay);
		}
	}

	//	search given string for a match to targetWord.
	//
	//	checks match to whole string or to any word in source.
	//
	//  distance encoding:
	//
	//		if source contains targetWord:  diff in len (0 is perfect match)
	//
	//		else: find word, in source that minimzes distance (can use whole expresion if it's better that a single word) 
	//
	//		100* (levenstien dist) + diff in len (prioritize missclicks over change in length)
	//
	//		-1 means too far.
	//
	private static int matchWord(String targetWord, String source, int max_err){
		source = source.toLowerCase();
		if(source.contains(targetWord)){
			String[] parts = source.split(" ");
			int min_len = source.length();
			for( String part: parts){//return diff len between shortest containing source-word, and targetWord
				if(part.contains(targetWord)){
					min_len =  Math.min(part.length(),min_len);
				}
			}
			return Math.abs(targetWord.length() - min_len);
		}
		String[] parts = source.split(" ");
		int temp_len = source.length();
		int dist = StringUtils.getLevenshteinDistance(
					(CharSequence)targetWord,(CharSequence)source,max_err);
		for( String part: parts){
			if(part.equals("")){
				continue;
			}
			int temp = StringUtils.getLevenshteinDistance(
					(CharSequence)targetWord,(CharSequence)part,max_err);
			if(dist == -1){//dist is trivial
				dist = temp;
				temp_len = part.length();
				continue;
			}if(temp == -1){//temp is trivial
				continue;
				
			}if(temp < dist){//temp is nontrivial minimum
				dist =  temp;
				temp_len = part.length();
			}
		}
		if(dist == -1){
			return -1;
		}else{//use distance and len of most matching part
			return 100*dist + Math.abs(targetWord.length() - temp_len);
		}
	}	
	//returns sum of distances of each word of target from string source
	//when distance of a word from string is minimal distance from a word
	//in the String;
	private static int matchString(String target, String source, int max_err){
		String[] parts = target.split(" ");
		int score=0;
		int temp_score;
		for(String part : parts){
			if(part.equals("")){
				continue;
			}
			temp_score = matchWord(part,source,max_err);
			if(temp_score == -1){//too long a distance
				return -1;
			}
			score+=temp_score;
		}
		if(score/100 > max_err){//too many misses overall
			return -1;
		}
		return score;
	}
}